package com.logistics.modules.transportationManagement.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.logistics.common.utils.R;
import com.logistics.modules.financialManagement.entity.FreightDetailsEntity;
import com.logistics.modules.financialManagement.entity.FreightEntity;
import com.logistics.modules.financialManagement.service.FreightDetailsService;
import com.logistics.modules.financialManagement.service.FreightService;
import com.logistics.modules.sys.service.FreightcalculaterulesService;
import com.logistics.modules.sys.service.SystemcodetableService;
import com.logistics.modules.transportationManagement.chart.KeyAndValueChart;
import com.logistics.modules.transportationManagement.entity.*;
import com.logistics.modules.transportationManagement.excel.ExcelCargoMapSeat;
import com.logistics.modules.transportationManagement.excel.ExcelMovePlan;
import com.logistics.modules.transportationManagement.service.ClientorderService;
import com.logistics.modules.transportationManagement.service.MoveplandetailsService;
import com.logistics.modules.warehouseManagement.service.SeatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.logistics.common.utils.PageUtils;
import com.logistics.common.utils.Query;
import com.logistics.modules.transportationManagement.dao.MoveplanDao;
import com.logistics.modules.transportationManagement.service.MoveplanService;
import org.springframework.transaction.annotation.Transactional;

/**
 * impl moveplan服务
 * @author 水穷处云起时
 * @date 2022/05/19
 */
@Service("moveplanService")
public class MoveplanServiceImpl
        extends ServiceImpl<MoveplanDao, MoveplanEntity>
        implements MoveplanService {

    /**
     * 位置服务
     */
    @Autowired
    private SeatService seatService;

    @Autowired
    private ClientorderService clientorderService;

    @Autowired
    private MoveplandetailsService moveplandetailsService;

    @Autowired
    private FreightService freightService;

    @Autowired
    private FreightDetailsService freightDetailsService;

    @Autowired
    private FreightcalculaterulesService freightcalculaterulesService;

    @Autowired
    private SystemcodetableService systemCodeService;

    /**
     * 分页查询页面
     *
     * @param params 参数个数
     * @return {@link PageUtils}
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        // 分页参数
        IPage<MoveplanEntity> page = new Query<MoveplanEntity>().getPage(params);

        // 获取查询数据
        String name = Convert.toStr(params.get("name"));
        String phone = Convert.toStr(params.get("phone"));
        Integer dataPlanMoney = Convert.toInt(params.get("dataPlanMoney"));
        Integer dataPlanStatus = Convert.toInt(params.get("dataPlanStatus"));
        Integer dataPlanCargoStatus = Convert.toInt(params.get("dataPlanCargoStatus"));
        String startAddress = Convert.toStr(params.get("startAddress"));
        String endAddress = Convert.toStr(params.get("endAddress"));
        Boolean status = Convert.toBool(params.get("status"));

        this.baseMapper.quaryPage
                (page, name, phone, dataPlanMoney, dataPlanStatus,
                        dataPlanCargoStatus, startAddress, endAddress, status);

        return new PageUtils(page);
    }

    /**
     * 入库和和生成运输计划
     *
     * @param params 参数个数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void warehousingAndGeneratingShippingPlans(Map<String, Object> params) {
        // 客户订单id
        Integer clientOrderId = Convert.toInt(params.get("clientOrderId"));

        // 仓库id
        Integer storehouse = Convert.toInt(params.get("storehouse"));

        // 入库并返回客户订单详情
        ClientorderEntity clientorderEntity =
                clientorderService.inventoryAndReturnCustomerOrderInformation(clientOrderId, storehouse);

        // 根据客户订单生成运输计划
        generateShippingPlan(clientorderEntity);
    }

    /**
     * 根据id选择所有
     *
     * @param movePlanIds 行动计划id
     * @return {@link List}<{@link MoveplanEntity}>
     */
    @Override
    public List<MoveplanEntity> selectAllByIds(Integer[] movePlanIds) {
        return this.baseMapper.selectBatchIds(Arrays.asList(movePlanIds));
    }

    /**
     * 修改运输计划的状态
     *
     * @param vehicleMoveOrder
     * @param status
     * @return {@link Boolean}
     */
    @Override
    public Boolean modifyTheStatusOfAShippingPlan(VehiclemoveorderEntity vehicleMoveOrder, Integer status) {
        // 通过车辆运输订单id获取 车辆运输订单详情
        List<VehiclemoveorderdetailsEntity> vehicleMoveOrderDetailsList = vehicleMoveOrder.getVehicleMoveOrderDetailsList();

        try {
            // 遍历根据运输计划id去修改获取状态
            for (VehiclemoveorderdetailsEntity vmo : vehicleMoveOrderDetailsList) {
                Integer movePlanId = vmo.getMoveplanId();
                // 更改状态
                this.baseMapper.updateCargoStatusById(status + 1, movePlanId);
            }

            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 获取结算的数据和计算逾期费用并保存到数据库
     *
     * @param params 参数个数
     * @return {@link PageUtils}
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public R getSettlementAndSaveDatabase(Map<String, Object> params) {
        // 1. 获取参数
        Integer movePlanId = Convert.toInt(params.get("movePlanId"));
        // 2. 计算逾期费用并保存到数据库
        MoveplanEntity movePlan = calculateOverdueChargesAndSaveToDatabase(movePlanId);
        // 3. 获取结算需要的数据
        return getTheDataNeededForSettlement(movePlan);
    }

    /**
     * 实现运输计划
     *
     * @param params 参数个数
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void settlementShippingPlan(Map<String, Object> params) {
        // 1. 获取数据
        Integer movePlanId = Convert.toInt(params.get("movePlanId"));
        Double theAmountActuallyPaid = Convert.toDouble(params.get("theAmountActuallyPaid"));

        if (movePlanId == null || theAmountActuallyPaid == null) {
            throw new RuntimeException("参数获取失败!");
        }
        // 2. 修改运输计划的 货物状态为已取货
        this.baseMapper.updateCargoStatusById(5, movePlanId);
        // 3. 完成运费表
        completeTheShippingSchedule(movePlanId, theAmountActuallyPaid);
        // 4. 完成运输计划
        finishMovePlan(movePlanId);
        // 5. 释放席位
        freeSeat(movePlanId);
    }

    /**
     * 下载excel
     *
     * @param map 行动计划id
     * @return {@link ExcelMovePlan}
     */
    @Override
    public ExcelMovePlan downloadExcel(Map<String, Object> map) {
        // 通过id查询出运输计划对象
        MoveplanEntity moveplanEntity = this.baseMapper.selectOneById(Convert.toInt(map.get("movePlanId")));
        ClientorderEntity clientOrder = moveplanEntity.getClientOrder();

        ExcelMovePlan excelMovePlan = new ExcelMovePlan();
        // 设置数据
        excelMovePlan.setConsigneeName(clientOrder.getConsigneeName());
        excelMovePlan.setConsigneePhone(clientOrder.getConsigneePhone());
        excelMovePlan.setCreateTime(moveplanEntity.getCreateTime());
        excelMovePlan.setConsignorName(clientOrder.getConsignorName());
        excelMovePlan.setConsignorPhone(clientOrder.getConsignorPhone());
        excelMovePlan.setFinishTime(moveplanEntity.getFinishTime());
        excelMovePlan.setStartAddress(clientOrder.getStartAddress());
        excelMovePlan.setEndAddress(clientOrder.getEndAddress());


        // 获取货物和席位对应信息的集合
        ArrayList<ExcelCargoMapSeat> list = new ArrayList<>();
        for (MoveplandetailsEntity details : moveplanEntity.getMovePlanDetailsList()) {
            ExcelCargoMapSeat excelCargoMapSeat = new ExcelCargoMapSeat();
            // 设置信息
            excelCargoMapSeat.setRepositoryName(details.getSeat().getRepository().getStoreName());
            excelCargoMapSeat.setCargoName(details.getCargo().getCargoName());
            excelCargoMapSeat.setSeatName(details.getSeat().getSeatName());
            // 添加到集合里
            list.add(excelCargoMapSeat);
        }
        excelMovePlan.setCargoMapSeatList(list);
        // 返回数据
        return excelMovePlan;
    }

    /**
     * 获取数据根据时间段
     *
     * @param params 参数个数
     * @return {@link R}
     */
    @Override
    public R getDataByTime(Map<String, Object> params) {
        // 1. 获取数据
        Integer period = Convert.toInt(params.get("period"));
        // 2. 校验数据
        if (period == null) {
            throw new RuntimeException("参数获取失败!");
        }
        // 3. 判断时间段
        switch (period) {
            case 1:
                // 获取当前七天的数据
                return getDataByDay(7);
            case 2:
                // 获取当前三十天的数据
                return getDataByDay(30);
            case 3:
                // 获取当前一年的数据
                return getDataByYear();
            case 4:
                // 获取全部年份的数据
                return getDataByAllYear();
        }

        throw new RuntimeException("参数获取失败!");
    }



    @Override
    public R getPieData() {
        // 1. 今天已完成的运输计划
        Integer countFinish = this.baseMapper.countByPlanStatusAndFinish(1);
        // 2. 今天未完成的运输计划
        Integer countUndone = this.baseMapper.countByPlanStatusAndUndone();
        ArrayList<KeyAndValueChart> keyAndValueCharts = new ArrayList<>();
        keyAndValueCharts.add(new KeyAndValueChart("已完成", countFinish.toString()));
        keyAndValueCharts.add(new KeyAndValueChart("未完成", countUndone.toString()));

        return R.ok().put("res", keyAndValueCharts);
    }

    /**
     * 获取全年的数据
     *
     * @return {@link R}
     */
    private R getDataByAllYear() {
        // 0. 总金额
        Integer totalMoney = 0;
        // 1. 去数据库那数据 去查询全部年份的数据
        List<KeyAndValueChart> movePlanCharts = this.baseMapper.selectSumMoneyByAllYear();
        // 2. 处理数据
        List<String> names = new ArrayList<>();
        List<String> values = new ArrayList<>();
        for (KeyAndValueChart movePlanChart : movePlanCharts) {
            names.add(movePlanChart.getName());
            values.add(movePlanChart.getValue() == null ? "0" : movePlanChart.getValue());
            // 累加totalMoeny
            totalMoney+=Convert.toInt(movePlanChart.getValue() == null ? "0" : movePlanChart.getValue());
        }
        // 2. 返回数据
        return R.ok().put("names", names).put("values", values).put("totalMoney",totalMoney);
    }

    /**
     * 在今年获得数据
     *
     * @return {@link R}
     */
    private R getDataByYear() {
        // 0. 总金额
        Integer totalMoney = 0;
        // 1. 去数据库那数据
        List<KeyAndValueChart> movePlanCharts = this.baseMapper.selectSumMoneyByYear();
        // 2. 处理数据
        List<String> names = new ArrayList<>();
        List<String> values = new ArrayList<>();
        for (KeyAndValueChart movePlanChart : movePlanCharts) {
            names.add(movePlanChart.getName() + "月");
            values.add(movePlanChart.getValue() == null ? "0" : movePlanChart.getValue());
            // 累加totalMoeny
            totalMoney+=Convert.toInt(movePlanChart.getValue() == null ? "0" : movePlanChart.getValue());
        }
        // 2. 返回数据
        return R.ok().put("names", names).put("values", values).put("totalMoney",totalMoney);
    }

    /**
     * 获取当前时间段的数据
     *
     * @param day 我
     * @return {@link R}
     */
    private R getDataByDay(int day) {
        // 0. 总金额
        Integer totalMoney = 0;
        // 1. 实例化集合
        // 2. 处理数据
        List<String> names = new ArrayList<>();
        List<String> values = new ArrayList<>();
        // 2. 遍历时间段
        for (int i = day - 1; i >= 0; i--) {
            // 3. 获取数据
            List<String> strings = this.baseMapper.selectSumMoneyByDate(i);
            // 4. 封装数据
            names.add(strings.get(1));
            values.add(strings.get(0) == null ? "0" : strings.get(0));
            // 累加totalMoeny
            totalMoney+=Convert.toInt(strings.get(0) == null ? "0" : strings.get(0));
        }
        // 6. 返回数据
        return R.ok().put("names", names).put("values", values).put("totalMoney",totalMoney);
    }

    /**
     * 释放该条运输计划所占用的所有席位
     *
     * @param movePlanId 行动计划id
     */
    private void freeSeat(Integer movePlanId) {
        // 查询出实体
        MoveplanEntity movePlan = this.baseMapper.selectOneById(movePlanId);
        // 获取运输计划详情
        List<MoveplandetailsEntity> movePlanDetailsList = movePlan.getMovePlanDetailsList();
        // 遍历循环释放席位
        for (MoveplandetailsEntity details : movePlanDetailsList) {
            seatService.releaseOccupation(details);
        }

    }

    /**
     * 完成行动计划
     *
     * @param movePlanId 行动计划id
     */
    private void finishMovePlan(Integer movePlanId) {
        // 修改完成时间 and 修改计划状态 and 修改 结算状态 根据 id
        this.baseMapper.updatePlanStatusAndCloseStatusAndFinishTimeById(1, 1, new Date(), movePlanId);
    }

    /**
     * 完成运费表的数据
     *
     * @param movePlanId            行动计划id
     * @param theAmountActuallyPaid 实际支付金额
     */
    private void completeTheShippingSchedule(Integer movePlanId, Double theAmountActuallyPaid) {
        freightService.completeTheShippingSchedule(movePlanId,  theAmountActuallyPaid);
    }

    /**
     * 得到所需数据结算
     *
     * @param movePlan 行动计划
     * @return {@link R}
     */
    private R getTheDataNeededForSettlement(MoveplanEntity movePlan) {
        R ok = R.ok();

        // 货物总运费
        Double money = movePlan.getFreight().getAccruedCharge();
        ok.put("totalMoney", money);
        // 效验是否有逾期费用
        if (movePlan.getFreight().getOverdueCharge() != null) {
            ok.put("lateFee", movePlan.getFreight().getOverdueCharge());
        }
        // 设置公共部分
        ok.put("consigneeName", movePlan.getClientOrder().getConsigneeName());
        ok.put("consigneePhone", movePlan.getClientOrder().getConsigneePhone());
        // 返回
        return ok;
    }

    /**
     * 计算逾期费用并保存到数据库中
     *
     * @param movePlanId 行动计划id
     * @return {@link MoveplanEntity}
     */
    private MoveplanEntity calculateOverdueChargesAndSaveToDatabase(Integer movePlanId) {
        // 1. 获取到运输计划实体类
        MoveplanEntity moveplanEntity = this.baseMapper.selectOneById(movePlanId);
        // 2. 读取席位保存货物的时间
        // 2.1 效验
        if (moveplanEntity == null || moveplanEntity.getMovePlanDetailsList().size() <= 0) {
            throw new RuntimeException("程序出错! 在计算逾期费用这里出错! 请联系管理员!");
        }
        // 2.2 获取数据
        Date cargoSaveDate = moveplanEntity.getMovePlanDetailsList().get(0).getSeat().getCargoSaveSeat();
        // 3. 通过现在的时间-保存的时间-默认可保存的时间得到是否逾期在 * 仓库默认保存费用
        Double lateFee = calculateLateFees(cargoSaveDate, moveplanEntity);
        // 4. 有则保存到仓库没有则不保存
        if (lateFee != null) {
            FreightEntity freight = moveplanEntity.getFreight();
            freight.setOverdueCharge(lateFee);
            // 计算总运费
            freight.setAccruedCharge(lateFee + freight.getCargoMoeny());
            // 保存到数据库
            freightService.updateById(freight);
            moveplanEntity.setFreight(freight);
        }
        return moveplanEntity;
    }

    /**
     * 计算滞纳金
     * 通过现在的时间-保存的时间-默认可保存的时间得到是否逾期在 * 仓库默认保存费用
     * @param cargoSaveDate 货物保存日期
     * @param moveplanEntity
     * @return {@link Double}
     */
    private Double calculateLateFees(Date cargoSaveDate, MoveplanEntity moveplanEntity) {
        // 1. 获取逾期费用
        Double lateFee = getOverdueFees(moveplanEntity);
        // 2. 获取默认可保存的时间
        Integer defaultTime = getDefaultSaveTime();
        // 3. 计算时间差
        long between = DateUtil.between(new Date(), cargoSaveDate, DateUnit.DAY);
        // 4. 使用时间差 - 默认保存时间
        long dateDay = between - defaultTime;
        // 5. 效验时间差 是否小于 0 小于零则返回null
        if (dateDay < 0) {
            return null;
        }

        // 计算逾期费用
        Double moeny = dateDay * lateFee;
        return moeny;
    }

    /**
     * 生成运输计划
     *
     * @param clientorderEntity clientorder实体
     */
    private void generateShippingPlan(ClientorderEntity clientorderEntity) {

        // 1. 生成运输计划实体 -- 没有添加运费表id
        MoveplanEntity moveplanEntity = generateShippingPlanEntity(clientorderEntity);

        // 2. 生成运费实体
        FreightEntity freightEntity = generateShippingEntity(moveplanEntity);

        // main -- 生成1和2的详情并保存到数据库中 完善1和2的数据
        generateShippingOrderDetailsFromCustomerOrderDetails
                (clientorderEntity, moveplanEntity, freightEntity);
    }

    /**
     * 负责生成运输计划详情和 运费详情。 同时把运费表id添加到运输计划表中
     * 给运费表添加运费价钱和应付费用
     *
     * @param clientorderEntity clientorder实体
     * @param moveplanEntity    moveplan实体
     * @param freightEntity     货物实体
     */
    private void generateShippingOrderDetailsFromCustomerOrderDetails(
            ClientorderEntity clientorderEntity, MoveplanEntity moveplanEntity,
            FreightEntity freightEntity) {

        // 这个运输计划的总金额(不包含逾期费用)
        Double totalMoney = 0.0;

        // 1. 遍历客户订单详情 根据客户订单详情生成对应的运输计划详情
        for (ClientorderdetailsEntity cod : clientorderEntity.getClientorderdetails()) {
            // 计划详情
            MoveplandetailsEntity moveplandetailsEntity = new MoveplandetailsEntity();

            // 为计划详情赋值
            moveplandetailsEntity.setSeatId(cod.getSeatId());
            moveplandetailsEntity.setMoveplanId(moveplanEntity.getId());
            moveplandetailsEntity.setCargoId(cod.getCargoId());

            //保存到数据库
            moveplandetailsService.save(moveplandetailsEntity);

            // 把运费详情保存到数据库
            FreightDetailsEntity freightDetails =
                    saveShippingDetailsToDatabase(moveplandetailsEntity, freightEntity, cod.getCargo(),clientorderEntity);

            // 修改计划详情的运费详情id
            moveplandetailsService.updateFreightDetailsIdById(freightDetails.getId(), moveplandetailsEntity.getId());

            // 累加总金额
            totalMoney += freightDetails.getMoney();
        }

        // 2. 设置总金额
        freightEntity.setCargoMoeny(totalMoney);
        freightEntity.setAccruedCharge(totalMoney);

        // 3. 把运费表的新增数据保存到数据库
        freightService.updateById(freightEntity);

        // 4. 把运费表的id添加到运输计划中并保存
        this.baseMapper.updateFrightIdById(freightEntity.getId(), moveplanEntity.getId());
    }

    /**
     * 把运费详情保存到数据库
     *
     * @param moveplandetailsEntity moveplandetails实体
     * @param freightEntity         货物实体
     * @param cargo                 货物
     * @param clientorderEntity     clientorder实体
     * @return {@link FreightDetailsEntity}
     */
    private FreightDetailsEntity saveShippingDetailsToDatabase(
            MoveplandetailsEntity moveplandetailsEntity, FreightEntity freightEntity,
            CargoEntity cargo, ClientorderEntity clientorderEntity) {

        FreightDetailsEntity freightDetailsEntity = new FreightDetailsEntity();

        // 根据货物得到运费计算规则
        Double money = freightcalculaterulesService.calculateTheShippingCostAccordingToTheGoods(cargo, clientorderEntity);

        // 设置金额 运输计划详情id 货物id
        freightDetailsEntity.setMoney(money);
        freightDetailsEntity.setMovePlanDetailsId(moveplandetailsEntity.getId());
        freightDetailsEntity.setCargoId(cargo.getId());
        // 保存到数据库
        freightDetailsService.save(freightDetailsEntity);
        //返回
        return freightDetailsEntity;
    }

    /**
     * 产生运输实体
     *
     * @param moveplanEntity moveplan实体
     * @return {@link FreightEntity}
     */
    private FreightEntity generateShippingEntity(MoveplanEntity moveplanEntity) {
        // 运费实体
        FreightEntity freightEntity = new FreightEntity();

        // 设置运输计划id
        freightEntity.setMovePlanId(moveplanEntity.getId());

        // 保存到数据库
        freightService.save(freightEntity);

        // 返回
        return freightEntity;
    }

    /**
     * 生成运输计划实体
     *
     * @param clientorderEntity clientorder实体
     * @return {@link MoveplanEntity}
     */
    private MoveplanEntity generateShippingPlanEntity(ClientorderEntity clientorderEntity) {
        // 运输计划
        MoveplanEntity moveplanEntity = new MoveplanEntity();

        // 设置运输计划属性

        moveplanEntity.setCloseStatus(0);
        moveplanEntity.setPlanStatus(0);
        moveplanEntity.setCargoStatus(0);
        moveplanEntity.setClientorderId(clientorderEntity.getId());
        moveplanEntity.setCreateTime(new Date());
        // 运输计划保存的数据库
        this.baseMapper.insert(moveplanEntity);

        return moveplanEntity;
    }

    /**
     * 得到默认保存时间
     *
     * @return {@link Integer}
     */
    public Integer getDefaultSaveTime() {
        return systemCodeService.getDefaultSaveTime();
    }

    /**
     * 得到逾期费用
     *
     * @return {@link Double}
     * @param moveplanEntity
     */
    public Double getOverdueFees(MoveplanEntity moveplanEntity) {
        return systemCodeService.getOverdueFees(moveplanEntity);
    }
}